package main

import (
	"encoding/json"
	"flag"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os/exec"
	"runtime"
	"strings"
)

//通过ip-api.com/json/获取公网ip时，返回的json数据结构
type Ip struct {
	Status      string  `json:"status"`
	Country     string  `json:"country"`
	Countrycode string  `json:"countryCode"`
	Region      string  `json:"region"`
	Regionname  string  `json:"regionName"`
	City        string  `json:"city"`
	Zip         string  `json:"zip"`
	Lat         float64 `json:"lat"`
	Lon         float64 `json:"lon"`
	Timezone    string  `json:"timezone"`
	Isp         string  `json:"isp"`
	Org         string  `json:"org"`
	As          string  `json:"as"`
	Query       string  `json:"query"`
}

var ()

func main() {
	var (
		a   Domain
		err error
	)
	//解析参数
	flag.Parse()

	/*if flag.NFlag() < 1 {
		flag.Usage()
		log.Fatalf("请输入正确的命令行参数，至少需要指定根域名、子域名、DNSPOD调用API的token")
	}*/

	if f.Domain == "" || f.SubDomain == "" {
		log.Fatalln("需要同时指定根域名和子域名，如动态DNS为：a.taobao.com，则参数要为：-domain taobao.com -record a")
	}

	//根据域名，生成临时文件的全路径和文件名
	if runtime.GOOS == "windows" {
		f.TmpFile = strings.Join([]string{"c:\\" + f.SubDomain, f.Domain}, ".")
	} else {
		f.TmpFile = strings.Join([]string{"/tmp/" + f.SubDomain, f.Domain}, ".")
	}
	if f.DnspodToken == "0" {
		log.Fatalln("Not Dnspod Token!!! \n Eixt!!")
	}
	if f.IP == "0" { //如果没有输入IP地址的参数
		f.IP, err = GetIp()
		if err != nil || f.IP == "" {
			log.Fatalln("程序取公网IP出错，退出！")
		}
		log.Printf("程序取得公网IP：%s \n", f.IP)
		//取得旧IP地址
		oldIP, _ := r()
		//比较旧的IP地址与新的IP地址，如果一致则退出程序
		if oldIP == f.IP {
			log.Printf("当前IP与上一次更新IP一样，不需要更新记录，程序退出！")
			return
		}
	}
	log.Printf("输入参数,ip：%s,子域名：%s，域名：%s", f.IP, f.SubDomain, f.Domain)
	///

	log.Println("start!解析:", f.SubDomain)
	client := &http.Client{}
	//获取本帐户所有的域名列表
	//post请求
	v := url.Values{}
	v.Add("login_token", f.DnspodToken)
	v.Add("format", "json")
	resp, err := client.PostForm("https://dnsapi.cn/Domain.List", v)
	defer resp.Body.Close()
	if err != nil {
		log.Println(err.Error())
	}
	if resp.StatusCode != 200 {
		log.Fatalln("从dnspod取域名列表出错！")
	}
	body, _ := ioutil.ReadAll(resp.Body)
	//log.Println(string(body))
	//post请求结束

	//解析JSON到结构体
	var d DomainList
	err = json.Unmarshal(body, &d)
	if err != nil {
		log.Fatal(err)
	}
	log.Println("取得本帐户所有域名列表消息：\n", d)

	for _, domain := range d.Domains {
		if domain.Name == f.Domain {
			a = domain
		}
	}
	log.Printf("本次运行使用的域名：%s 的ID号：%d，域名状态：%s \n", f.Domain, a.Id, a.Status)
	if a.Status == "lock" || a.Status == "spam" || a.Status == "pause" {
		log.Panicf("域名状态出错，需要手动解除！")
	} else if a.Status == "enable" {
		log.Printf("域名状态正常！")
	} else {
		log.Fatalf("域名：%s，状态不正确，检查域名是否正确！", f.Domain)
	}

	//获取单个域名信息
	log.Printf("取得单个域名 %s 的信息：\n", f.Domain)
	v = url.Values{}
	v.Add("login_token", f.DnspodToken) //公共参数，必要
	v.Add("format", "json")             //公共参数，必要
	v.Add("domain", f.Domain)           //或用domain_id也可以：v.Add("domain_id", strconv.Itoa(a.Id))
	body, err = post("https://dnsapi.cn/Domain.Info", v)
	log.Printf(string(body))

	//取得域名的记录列表
	log.Printf("取记录 %s 的信息：--------------------------\n", f.SubDomain)
	v = url.Values{}
	v.Add("login_token", f.DnspodToken) //公共参数，必要
	v.Add("format", "json")             //公共参数，必要
	v.Add("domain", f.Domain)           //或用domain_id也可以：v.Add("domain_id", strconv.Itoa(a.Id))
	v.Add("sub_domain", f.SubDomain)    //sub_domain 子域名，如果指定则只返回此子域名的记录，可选
	body, err = post("https://dnsapi.cn/Record.List", v)
	log.Printf("记录列表：\n", string(body))
	//解析JSON到结构体
	var g GetRecordRet
	var b Record
	err = json.Unmarshal(body, &g)
	if err != nil {
		log.Fatal(err)
	}
	for _, record := range g.Records {
		//log.Printf("子域名id：%d,name:%s \n", record.Id, record.Name)
		if record.Name == f.SubDomain {
			b = record
		}
	}
	log.Printf("取得域名%s的子域名[%s]的记录，子域名id：%s,name:%s \n", f.Domain, f.SubDomain, b.Id, b.Name)

	//修改域名的记录（子域名）的IP地址
	log.Printf("执行修改记录的IP地址，记录：%s，IP：%s \n", f.SubDomain, f.IP)
	v = url.Values{}
	v.Add("login_token", f.DnspodToken) //公共参数，必要
	v.Add("format", "json")             //公共参数，必要
	v.Add("domain", f.Domain)           //或用domain_id也可以：v.Add("domain_id", strconv.Itoa(a.Id))
	v.Add("record_id", b.Id)            //record_id 记录ID，必选
	v.Add("sub_domain", f.SubDomain)    //sub_domain 子域名，如果指定则只返回此子域名的记录，可选
	v.Add("value", f.IP)                //value 记录值, 如 IP:200.200.200.200, CNAME: cname.dnspod.com., MX: mail.dnspod.com.，必选
	v.Add("record_type", "A")           //record_type 记录类型，通过API记录类型获得，大写英文，比如：A，必选
	v.Add("record_line", "默认")          //record_line 记录线路，通过API记录线路获得，中文，比如：默认，必选
	//record_line_id 线路的ID，通过API记录线路获得，英文字符串，比如：‘10=1’ 【record_line 和 record_line_id 二者传其一即可，系统优先取 record_line_id】
	body, err = post("https://dnsapi.cn/Record.Modify", v)
	log.Printf("修改记录返回数据：%s\n", string(body))
	//解析JSON到结构体
	var c RecordModifyRet
	err = json.Unmarshal(body, &c)
	if err != nil {
		log.Fatal(err)
	}
	if c.Status.Code == "1" {
		log.Printf("恭喜：域名%s的记录%s修改ip为%s成功，更新的IP地址：%s", f.Domain, f.SubDomain, f.IP, c.Record.Value)
		//将更新的IP地址保存到临时文件
		w(f.IP)
	} else {
		log.Fatalln("修改记录失败，检查提交的record值是否正确！")
	}

	//执行脚本
	cmd := exec.Command(f.Script)
	if err := cmd.Run(); err != nil {
		log.Printf("启动脚本程序%s失败！\n", f.Script)
		return
	}
}

func post(urlstr string, v url.Values) (body []byte, err error) {
	client := &http.Client{}
	//post请求
	//v := url.Values{}
	//v = postvalues.(url.Values)	//从interface{}转换到url.values类型
	resp, err := client.PostForm(urlstr, v)
	defer resp.Body.Close()
	if err != nil {
		log.Println(err.Error())
	}
	if resp.StatusCode != 200 {
		log.Panicf("post出错！返回代码不为200！")
	}
	body, _ = ioutil.ReadAll(resp.Body)
	//log.Println(string(body))
	//post请求结束
	return body, err
}

//通过http://ip-api.com/json/获取公网ip
func GetIp() (string, error) {
	resp, err := http.Get(f.ParUrl)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	content, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Println("query internet ip err! ", err)
		return "", err
	}
	log.Printf("%s", content)
	var ip Ip
	json.Unmarshal([]byte(content), &ip)
	return ip.Query, err
}

//通过http://myexternalip.com/raw获取公网ip
func GetIp1() (string, error) {
	resp, err := http.Get("https://myexternalip.com/raw")
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	content, err := ioutil.ReadAll(resp.Body)
	//buf := new(bytes.Buffer)
	//buf.ReadFrom(resp.Body)
	//s := buf.String()
	return string(content), err
}
